﻿using System;
using System.Linq;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Routing;
using UIShell.OSGi;

namespace Isaac.Joy.Mvc.Web
{
    public class BundleRazorViewEngine : RazorViewEngine
    {
        private readonly string[] _baseAreaViewLocationFormats;
        private readonly string[] _baseAreaMasterLocationFormats;
        private readonly string[] _baseAreaPartialViewLocationFormats;
        private readonly string[] _baseViewLocationFormats;
        private readonly string[] _baseMasterLocationFormats;
        private readonly string[] _basePartialViewLocationFormats;

        public BundleRazorViewEngine()
        {
            _baseAreaViewLocationFormats = AreaViewLocationFormats;
            _baseAreaMasterLocationFormats = AreaMasterLocationFormats;
            _baseAreaPartialViewLocationFormats = AreaPartialViewLocationFormats;
            _baseViewLocationFormats = ViewLocationFormats;
            _baseMasterLocationFormats = MasterLocationFormats;
            _basePartialViewLocationFormats = PartialViewLocationFormats;
        }

        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName,
            string masterName, bool useCache)
        {
            string symbolicName = null;
            object area;
            if (controllerContext.RouteData.DataTokens.TryGetValue("area", out area))
            {
                symbolicName = area as string;
            }
            else
            {
                var routeWithArea = controllerContext.RouteData.Route as IRouteWithArea;
                if (routeWithArea != null)
                {
                    symbolicName = routeWithArea.Area;
                }
                var castRoute = controllerContext.RouteData.Route as Route;
                if (castRoute != null && castRoute.DataTokens != null)
                {
                    symbolicName = castRoute.DataTokens["area"] as string;
                }
            }

            if (string.IsNullOrEmpty(symbolicName)) return new ViewEngineResult(new string[0]);

            var bundle = BundleRuntime.Instance.Framework.GetBundleBySymbolicName(symbolicName);
            if (bundle == null) return new ViewEngineResult(new string[0]);

            SetLocationFormats(@"~\" + bundle.Location.Replace(HostingEnvironment.ApplicationPhysicalPath, String.Empty));
            return base.FindView(controllerContext, viewName, masterName, useCache);
        }

        public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            string symbolicName = null;
            object area;
            if (controllerContext.RouteData.DataTokens.TryGetValue("area", out area))
            {
                symbolicName = area as string;
            }
            else
            {
                var routeWithArea = controllerContext.RouteData.Route as IRouteWithArea;
                if (routeWithArea != null)
                {
                    symbolicName = routeWithArea.Area;
                }
                var castRoute = controllerContext.RouteData.Route as Route;
                if (castRoute != null && castRoute.DataTokens != null)
                {
                    symbolicName = castRoute.DataTokens["area"] as string;
                }
            }

            if (string.IsNullOrEmpty(symbolicName)) return new ViewEngineResult(new string[0]);

            var bundle = BundleRuntime.Instance.Framework.GetBundleBySymbolicName(symbolicName);
            if (bundle == null) return new ViewEngineResult(new string[0]);

            SetLocationFormats(@"~\" + bundle.Location.Replace(HostingEnvironment.ApplicationPhysicalPath, String.Empty));
            return base.FindPartialView(controllerContext, partialViewName, useCache);
        }

        private void SetLocationFormats(string bundleLocation)
        {
            AreaViewLocationFormats = _baseAreaViewLocationFormats
                .Select(item => item.Replace("~", bundleLocation)).ToArray();
            AreaMasterLocationFormats = _baseAreaMasterLocationFormats
                .Select(item => item.Replace("~", bundleLocation)).ToArray();
            AreaPartialViewLocationFormats = _baseAreaPartialViewLocationFormats
                .Select(item => item.Replace("~", bundleLocation)).ToArray();
            ViewLocationFormats = _baseViewLocationFormats
                .Select(item => item.Replace("~", bundleLocation)).ToArray();
            MasterLocationFormats = _baseMasterLocationFormats
                .Select(item => item.Replace("~", bundleLocation)).ToArray();
            PartialViewLocationFormats = _basePartialViewLocationFormats
                .Select(item => item.Replace("~", bundleLocation)).ToArray();
        }
    }
}